enum StorageType {
  LocalStorage,
  SessionStorage
}

class Cache {
  storage: Storage;

  /**
   * @constructor 构造函数
   * @param type 用于指定存储类型，l表示localStorage，s表示sessionStorage，默认为localStorage
   */
  constructor(type: StorageType = StorageType.LocalStorage) {
    this.storage = type === StorageType.LocalStorage ? window.localStorage : window.sessionStorage;
  }

  /**
   * 将一个JS对象序列化为JSON字符串。
   * @param value - 需要被序列化的项目。
   * @returns 序列化后的JSON字符串。
   */
  serializeItem(value: any) {
    return JSON.stringify(value);
  }

  /**
   * 将一个JSON字符串反序列化为JS对象
   * @param value - 需要被反序列化的JSON字符串。
   * @return 反序列化后的JS对象。
   */
  deserializeItem<T>(value: string): T {
    return JSON.parse(value);
  }

  /**
   * 获取存储在Storage中的json字符串，将其反序列化为js对象。
   * @returns 存储在Storage中的对象。
   */
  getObjFromStorage<T>(key: string): T | null {
    const objStr = this.storage.getItem(key);
    if (objStr === null) {
      return null;
    }
    return this.deserializeItem<T>(objStr) as T;
  }

  /**
   * 将对象存储在Storage中。
   * @param key 要设置的一级数据的键
   * @param value 要存储的对象。
   */
  setObjToStorage(key: string, value: any) {
    const str = this.serializeItem(value);
    this.storage.setItem(key, str);
  }

  /**
   * 获取Storage中的数据
   * @param  key 要获取的数据的键
   * @param  isObj 是否是对象，默认不是对象
   */
  get<T = string>(key: string, isObj = false): T | string | null {
    if (!isObj) {
      return this.storage.getItem(key) as string | null;
    } else {
      return this.getObjFromStorage<T>(key) as T | null;
    }
  }

  /**
   * 设置Storage中的数据
   * @param key 要设置的数据的键
   * @param value 要设置的数据的值,可以是对象
   */
  set(key: string, value: any) {
    if (typeof value === 'object') {
      this.setObjToStorage(key, value);
    } else {
      this.storage.setItem(key, value);
    }
  }

  remove(key: string) {
    this.storage.removeItem(key);
  }

  clear() {
    this.storage.clear();
  }
}

export const storage = new Cache();
